
#include "analisador.h"

int cont_linhas = 1;

//**********************************************************************//
//****************************LEXICO************************************//

//verifica se o token que esta sendo lido eh uma string (entre "")
int token_string(char *op_token,char *char_lido,char *id_token,FILE *pFile)
{
    int achou_aspas_linha;

    achou_aspas_linha = cont_linhas;
    strncat(op_token,char_lido,1);
    *char_lido = fgetc(pFile);

    while (*char_lido != '"')
    {
        strncat(op_token,char_lido,1);
        if (*char_lido == '\\')
        {
            *char_lido = fgetc(pFile);
            strncat(op_token,char_lido,1);
        }
	else if (*char_lido == '\n')
	{
	    fprintf(stderr,"Linha %d: Faltando fechar caracter \".\n", achou_aspas_linha);
	    return 0;
	}
        *char_lido = fgetc(pFile);
        if (feof(pFile))
        {
            fprintf (stderr,"Linha %d: Faltando fechar caracter \".\n", achou_aspas_linha);
            return 0;
        }
    }
    strncat(op_token,char_lido,1);
    *char_lido = fgetc(pFile);
    id_token = "str";
    return 1;
}

//verifica se o token que esta sendo lido eh um char (entre '')
int token_char(char *op_token,char *char_lido,char *id_token,FILE *pFile)
{
    strncat(op_token,char_lido,1);
    *char_lido = fgetc(pFile);
    strncat(op_token,char_lido,1);
    if (*char_lido == '\\')
    {   
        *char_lido = fgetc(pFile);
        strncat(op_token,char_lido,1);
    }
    *char_lido = fgetc(pFile);
    if (*char_lido != '\'')
    {   
        fprintf (stderr,"Linha %d: Erro com caracter \'.\n", cont_linhas);
        return 0 ;
    }
    else
    {   
        strncat(op_token,char_lido,1);
        *char_lido = fgetc(pFile);
        id_token = "chr";
        return 1;
    }
}

//Testa se palavra pertence a lista de palavras reservadas
int TestaPalavraReservada(char* op_token)
{
    if((strcmp(op_token,"for") == 0) || (strcmp(op_token,"if") == 0) ||
    (strcmp(op_token,"while") == 0) || (strcmp(op_token,"else") == 0) ||
    (strcmp(op_token,"do") == 0) || (strcmp(op_token,"int") == 0) ||  
    (strcmp(op_token,"float") == 0) || (strcmp(op_token,"char") == 0) || 
    (strcmp(op_token,"main") == 0) || (strcmp(op_token,"return") == 0) ||
    (strcmp(op_token,"break") == 0) || (strcmp(op_token,"case") == 0) || 
    (strcmp(op_token,"sizeof") == 0) || (strcmp(op_token,"struct") == 0) ||
    (strcmp(op_token,"switch") == 0) || (strcmp(op_token,"bool") == 0) ||
    (strcmp(op_token,"NULL") == 0))
        return 1;
    else
        return 0;
}

//Funcao que testa caracteres duplos
char *testa_duplos (char char_lido, FILE *pFile)
{

    char *token;
    char operadores[]="+-|&=!<>/";
    char * op_token;
    char *id_token;
    int linha_atual;

    token = (char *) calloc (1,sizeof(char)*NUMCHARS);
    op_token = (char *) calloc (1,sizeof(char)*NUMCHARS);

    if (strchr(operadores,(int)char_lido))
    {
        strncat(op_token,&char_lido,1);
        char_lido = fgetc(pFile);
        strncat(op_token,&char_lido,1);

        if (strcmp(op_token,"!=") == 0 || strcmp(op_token,"==") == 0)
            id_token = "op_relac";

        else if (strcmp(op_token,">>") == 0 || strcmp(op_token,"<<") == 0)
            id_token = "op_desloc";

        else if (strcmp(op_token,"++") == 0 || strcmp(op_token,"--") == 0)
            id_token = "op_incre";

        else if (strcmp(op_token,"||") == 0 || strcmp(op_token,"&&") == 0)
            id_token = "op_logic";

        //Comentario ignorado
        else if (strcmp(op_token,"//") == 0)
        {
            while (char_lido != '\n')
                char_lido = fgetc(pFile);

            cont_linhas++;
            return "NULL";
        }

        //Comentario ignorado
        else if (strcmp(op_token,"/*") == 0)
        {
            linha_atual = cont_linhas;            
            while (!feof(pFile))
            {
                if (char_lido == '\n')
                    cont_linhas++;

                if (char_lido == '*')
                {
                    char_lido = fgetc(pFile);
                    if (char_lido == '/')
                    return "NULL";
                }
                char_lido = fgetc(pFile);
            }
            fprintf (stderr,"Linha %d: Erro com comentario não terminado.\n", linha_atual);

            return "END";
        }   
        else
        {
            fseek (pFile,-1,SEEK_CUR);
            return "";
        }


        sprintf(token,"<%s,%s>",id_token,op_token);

        return token;
    }

    free(token);
    free(op_token);
    return "";

}

//Funcao operador verifica a qual operador o presente token pertence
TOperador operador (char char_lido)
{
    TOperador tp_token;    
    char op_aritimeticos[]="+-*/%";
    char op_relacionais[]="<>";
    char op_caract_especiais[]="()[]{}#";
    char op_delimitadores[]=",;";
    char op_logicos[]="&|!";
    char op_descartaveis[]=" \t\n\0";

    if (strchr(op_relacionais,(int)char_lido)){
        tp_token.id_token = "op_relac";
        tp_token.op_tipo = OP_RELAC;
    }
    else if (strchr(op_aritimeticos,(int)char_lido)){    
        tp_token.id_token = "op_aritm";
        tp_token.op_tipo = OP_ARITM;
    }   
    else if (strchr(op_logicos,(int)char_lido)){
        tp_token.id_token = "op_logic";
        tp_token.op_tipo = OP_LOGICO;
    }
    else if (strchr(op_delimitadores,(int)char_lido)){
        tp_token.id_token =  "";
        tp_token.op_tipo = OP_DELIMIT;
    }
    else if (strchr(op_descartaveis,(int)char_lido)){
        tp_token.id_token =  "";
        tp_token.op_tipo = OP_DESCART;
    }
    else if (strchr(op_caract_especiais,(int)char_lido)){
        tp_token.id_token =  "";
        tp_token.op_tipo = OP_CARAC_ESP;
    }
    else if (char_lido == '='){
        tp_token.id_token = "";
        tp_token.op_tipo = OP_RELAC;
    }
    else
    {
        tp_token.id_token = NULL;
        tp_token.op_tipo = 0;
    }
    return tp_token;
}

// Analisador Lexico
char *lexico(FILE* pFile)
{
    char char_lido;
    char *token;
    char *aux_token;
    char * op_token;
    TOperador tp_token;

    //aloca memoria para o token
    token = (char *) calloc (1,sizeof(char)*NUMCHARS);
    op_token = (char *) calloc (1,sizeof(char)*NUMCHARS);

    //le char do arquivo e adiciona em char_lido
    char_lido = fgetc(pFile);

    //Identifica se char eh token
    tp_token = operador(char_lido);

    //le equanto encontrar algum caracter que represente um token isoladamente
    while (tp_token.op_tipo)
    {

        //monta token para o operador encontrado
        if (tp_token.op_tipo != OP_DESCART)
        {
            //=====>>>>> Testa duplos, como ++ && // etc...
            aux_token = testa_duplos(char_lido,pFile);
            if (strcmp(aux_token,"") != 0)
                return aux_token;

            //se não for duplo retorna token com o delimitador encontrado
            strncat(op_token,&char_lido,1);
           
            if (tp_token.id_token == "")
                sprintf(token,"<%s,%s>",op_token,tp_token.id_token);
            else
                sprintf(token,"<%s,%s>",tp_token.id_token,op_token);
            return token;
        }

        //se algum delimitador descartavel, le proximo char
        else if (tp_token.op_tipo == OP_DESCART)
        {
            if (char_lido == '\n')
                cont_linhas++;

            char_lido = fgetc(pFile);

            tp_token = operador(char_lido);
        }
    }

    //verifica se o token comeca com numero, se for eh token de numero, se encontrar algo diferente
    //no meio ou eh ponto para demarcar float ou eh erro
    if (isdigit(char_lido))
    {
        int float_num = 0;
        tp_token.id_token = "";        
        while (isdigit(char_lido))
        {
            strncat(op_token,&char_lido,1);
            char_lido = fgetc(pFile);

            tp_token = operador(char_lido);            

            //se encontrou algum delimitador
            if (tp_token.op_tipo)
            {
                //encontrando delimitador significa que o token atual eh realmente um numero
                //inteiro ou flutuante
								if (float_num)
	                tp_token.id_token = "Num_Float";					
								else
	                tp_token.id_token = "Num";
                break;
            }
            else if (char_lido == '.')
            {
                //encontrou ponto, verifica se ponto ja avia sido encontrado anteriormente
                if (float_num)
                {
                    //se ponto foi encontrado pela segunda vez... erro
                    fprintf (stderr,"Linha %d: Erro com numero flutuante invalido.\n", cont_linhas);
                    return "NULL";
                }   
                strncat(op_token,&char_lido,1);
                char_lido = fgetc(pFile);

                tp_token = operador(char_lido);

                //identifica que ponto foi encontrado uma vez, logo eh flutuante
                float_num = 1;
            }
        }

        if (tp_token.id_token == NULL)
        {    
            fprintf (stderr,"Linha %d: Erro com numero invalido.\n",cont_linhas);
            return "NULL";
        }
     }

    //Variavel utilizada no while abaixo para verificar se o id eh um numero
    while ((tp_token.op_tipo == 0) && !feof(pFile))
    {
        //se char " encontrado corresponde a inicio de string
        if (char_lido == '"'){
            tp_token.id_token = "str";
            tp_token.op_tipo = token_string(op_token,&char_lido,tp_token.id_token,pFile);
        }
        //se char ' encontrado corresponde a um char
        else if (char_lido == '\''){
            tp_token.id_token = "str";
            tp_token.op_tipo = token_char(op_token,&char_lido,tp_token.id_token,pFile);
        }
        //se palavra reservada, constantes ou variaveis
        else
        {
            //aqui separa os identificadores, e estes somente sao aceitos se compostos por letras
            //maisuculas, minusculas, underscore e numeros. Numero no primeiro já foi tratado
            if ((((int)char_lido >= 65) && ((int)char_lido <= 90))/*A-Z*/ || 
            (((int)char_lido >= 97) && ((int)char_lido <= 122))/*a-z*/ || ((int)char_lido ==
            95)/*'_'*/ || (((int)char_lido >= 48) && ((int)char_lido <= 57)) /*0-9*/)
            {
                strncat(op_token,&char_lido,1);
                char_lido = fgetc(pFile);

                tp_token = operador(char_lido);      

                //se for alguma palavra reservada imprime somente a palavra no token
                if (TestaPalavraReservada(op_token))
                    tp_token.id_token = "";
                else
                    tp_token.id_token = "id";
            }
            else
            {
                fprintf (stderr,"Linha %d: Erro caracter %c encontrado.\n",cont_linhas,char_lido);
                return "NULL";
            }
        }
    } 

    if (!feof(pFile))
        fseek (pFile,-1,SEEK_CUR); 
    else
        return "END";

    //verifica o identificador de token para montar a o token
    if (strcmp(tp_token.id_token,"") == 0)
        sprintf(token,"<%s,%s>",op_token,tp_token.id_token);
    else
        sprintf(token,"<%s,%s>",tp_token.id_token,op_token);

    return token;
}

